<?php
/*
 * Created on 21.12.2005
 *
 */

class PSPParser {

    var $lexer;
    var $tokenlist;
    var $tagstack;
    var $parentArray;
    var $basedir;
    var $ellexer;
    var $elparser;
    
    function PSPParser($lex, $basedir)
    {
        $this->lexer   = $lex;
        $this->basedir = $basedir;
    }
    
    function top ()
    {
        return $this->tagstack[count($this->tagstack)-1];
    }
    
    function parse ()
    {
        while ($this->lexer->hasNextToken())
        {
            $this->lexer->nextToken();
            $token = $this->lexer->getNextToken();
            if ($token['type'] == 'T_CDATA')
            {
                $this->tokenlist[] = $token;
                $this->cData($token['value']);
            }
            if ($token['type'] == 'T_SCRIPTLET')
            {
                $this->tokenlist[] = $token;
                $this->appendCode($token['value']);
            }
            if ($token['type'] == 'T_EL_CODE')
            {
                $this->tokenlist[] = $token;
                $lex    = new ELLexer($token['value']);
                $lex->lex();
                $parser = new ParserMachine($lex, $GLOBALS['parsertable'], $GLOBALS['parserproductions']);
                $call   = new ELCallback(); 
                if (!$parser->run($call))
                {
                    $this->appendCode('print "<b>PSPParser:</b> Error in EL Expression in line '.$token['line'].$token['value'].'!<br/>\n";');
                }
                else
                    $this->appendCode('print '.$call->getCode().';');
            }
            elseif ($token['type'] == 'T_BEGIN_OPEN_TAG')
            {
                $tag = array();
                if (!$this->lexer->hasNextToken()) 
                {
                    $this->lexer->nextToken();
                    $idT = $this->lexer->getNextToken();
                    die ("Tag name expected in line" . $idT['line'] . "\n");
                }
                $this->lexer->nextToken();
                $idT = $this->lexer->getNextToken();
                if ($idT['type'] == 'T_IDENTIFIER')
                {
                    $this->tagstack[] = $idT['value'];
                    $tag['type']  = 'T_OPENTAG';
                    $tag['name']  = $idT['value'];
                    $tag['ns']    = $token['ns'];
                    $tag['attrs'] = $this->attributes();
                }
                $end = $this->lexer->getNextToken();
                if ($end['type'] == 'T_END_TAG')
                {
                    $this->tokenlist[] = $tag;
                    $this->startElement($tag['ns'], $tag['name'], $tag['attrs']);
                }
                elseif ($end['type'] == 'T_END_FIN_TAG')
                {
                    $this->tokenlist[] = $tag;
                    $this->startElement($tag['ns'], $tag['name'], $tag['attrs']);
                    $tag = array();
                    $tag['type']  = 'T_CLOSE_TAG';
                    $tag['ns']    = $token['ns'];
                    $tag['name']  = $idT['value'];
                    array_pop($this->tagstack);
                    $this->tokenlist[] = $tag;
                    $this->endElement($tag['ns'], $tag['name']);
                }
            }
            elseif ($token['type'] == 'T_BEGIN_CLOSE_TAG')
            {
                $tag = array();
                if (!$this->lexer->hasNextToken()) 
                {
                    $this->lexer->nextToken();
                    $idT = $this->lexer->getNextToken();
                    print "<b>PSPParser:</b> Tag name expected in line" . $idT['line'] . "\n";
                    return;
                }
                $this->lexer->nextToken();
                $idT = $this->lexer->getNextToken();
                if ($idT['type'] == 'T_IDENTIFIER')
                {
                    if ($this->top() != $idT['value']) 
                    {
                        print "<b>PSPParser:</b> ".$token['ns'].":" . $this->top() . " tag expected in line " 
                            . $idT['line'] . " but " . $idT['value'] . " found\n";
                        return;
                    } 
                    else 
                    {
                        array_pop($this->tagstack);
                    }
                    $tag['type']  = 'T_OPENTAG';
                    $tag['ns']    = $token['ns'];
                    $tag['name']  = $idT['value'];
                    $this->tokenlist[] = $tag;
                    $this->endElement($tag['ns'], $tag['name']);
                }
                $this->lexer->nextToken();
                $end = $this->lexer->getNextToken();
                if ($end['type'] != 'T_END_TAG')
                {
                     print "<b>PSPParser:</b> '&gt;' expected in line " . $end['line'] . "\n";
                     return;
                }
            }
        }
        if (count($this->tagstack) > 0)
        {
            print "<b>PSPParser:</b> ".$token['ns'].":" . $this->top() . " tag expected at line " . $this->tokenlist[count($this->tokenlist)-1]['line'] . "\n";
        }
    }
    
    function attributes ()
    {
        if ($this->lexer->hasNextToken())
        {
            $this->lexer->nextToken();
            $name = $this->lexer->getNextToken();
            $attrs = array();
            while ($name['type'] == 'T_IDENTIFIER')
            {
                list($n, $v) = $this->attribute();
                $attrs[$n] = $v;
                $this->lexer->nextToken();
                $name = $this->lexer->getNextToken();
            }
            return $attrs;
        }
        else
        {
            $name = $this->lexer->getNextToken();
            print "<b>PSPParser:</b> Attribute, '>' or '/>' expected in line " . $name['line'] . "\n";
            $this->stopParse();
            return array();
        }
    }
    
    function attribute ()
    {
        $name   = array();
        $assign = array();
        $value  = array();
        $name = $this->lexer->getNextToken();
        if ($name['type'] == 'T_IDENTIFIER')
        {
            $this->lexer->nextToken();
            $assign = $this->lexer->getNextToken();
            if ($assign['type'] == 'T_ASSIGN')
            {
                $this->lexer->nextToken();
                $value = $this->lexer->getNextToken();
                if ($value['type'] == 'T_VALUE')
                {
                    $lex    = new ELLexer($value['value']);
                    $lex->lex();
                    $parser = new ParserMachine($lex, $GLOBALS['parsertable'], $GLOBALS['parserproductions']);
                    $call   = new ELCallback(); 
                    if (!$parser->run($call))
                    {
                        print "<b>PSPLexer:</b> Error in EL Expression in line ".$value['line']."<br/>\n";
                        return array ($name['value'], '""');
                    }
                    else
                        return array ($name['value'], $call->getCode());
                }
                else 
                {
                    print "<b>PSPParser:</b> Attribute value surrounded by \" expected in line " . $value['line'] . "\n";
                    $this->stopParse();
                    return array ($name['value'], '""');
                }
            }
            else 
            {
                print "<b>PSPParser:</b> Assignment expected in line " + $assign['line'] . "\n";
                $this->stopParse();
                return array ($name['value'], '""');
            }            
        }
    }
    
    function stopParse() {
        $this->stopParse = true;
    }
    
    function startElement ($namespace, $name, $attrs)
    {
        if (isset($GLOBALS['PSPhandler'][$namespace."_".$name]) 
            && is_object($GLOBALS['PSPhandler'][$namespace."_".$name]) 
            && method_exists($GLOBALS['PSPhandler'][$namespace."_".$name], 'startElement'))
        {
            $GLOBALS['PSPhandler'][$namespace."_".$name]->startElement($this, $attrs);
        }
        else
        {
            $classname = $name.'Handler';
            $path = $this->basedir.'tags/';
            if (!file_exists($path.$namespace."/".$classname.'.o.inc.php'))
                $path = $GLOBALS['cfgPaths']['lib'].'model2/tags/';
            if (file_exists($path.$namespace."/".$classname.'.o.inc.php'))
            {
                require_once ($path.$namespace."/".$classname.'.o.inc.php');
                $classname = $namespace."_".$classname;
                $GLOBALS['PSPhandler'][$namespace."_".$name] = new $classname;
                $GLOBALS['PSPhandler'][$namespace."_".$name]->startElement($this, $attrs);
            }
            else
                echo "<b>PSPParser:</b> No handler exists for tag $name\n" . $path .$namespace."/".$classname.'.o.inc.php';
        }
    }

    function endElement ($namespace, $name)
    {
        if (is_object($GLOBALS['PSPhandler'][$namespace."_".$name]) 
            && method_exists($GLOBALS['PSPhandler'][$namespace."_".$name], 'endElement'))
        {
            $GLOBALS['PSPhandler'][$namespace."_".$name]->endElement($this);
        }
        else
            echo "<b>PSPParser:</b> No handler exists for $name\n";
    }
    
    function cData ($data)
    {
        $this->appendCode("\n print \"".preg_replace('/"/','\"',$data)."\";\n");
        //print $data;
    }

    function pushParent(& $parent)
    {
        $this->parentArray[] = & $parent;
    }

    function popParent()
    {
        if (count($this->parentArray) > 0)
            array_pop($this->parentArray);
    }

    function topParent()
    {
        if (count($this->parentArray) > 0)
            return $this->parentArray[count($this->parentArray) - 1];
    }

    function appendCode ($code)
    {
        $this->appCode .= $code;
    }
    
    function getCode ()
    {
        return $this->appCode;
    }

}

?>
